From b313e6d164197e50ab08ce70e425664bd4804dc4 Mon Sep 17 00:00:00 2001 From: robertl Date: Tue, 14 Sep 2004 15:49:17 +0000 Subject: [PATCH] Initial sketching in of Garmin/USB modules. --- jeeps/gpsusbread.c | 62 ++++++++++ jeeps/gpsusbsend.c | 55 +++++++++ jeeps/gpsusbwin.c | 294 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 411 insertions(+) create mode 100644 jeeps/gpsusbread.c create mode 100644 jeeps/gpsusbsend.c create mode 100644 jeeps/gpsusbwin.c diff --git a/jeeps/gpsusbread.c b/jeeps/gpsusbread.c new file mode 100644 index 000000000..6c9284dec --- /dev/null +++ b/jeeps/gpsusbread.c @@ -0,0 +1,62 @@ +/* + Decompose an incoming USB packet to make it look like a serial one. + + Copyright (C) 2004 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include "gps.h" +#include "garminusb.h" + +int32 GPS_Packet_Read_usb(int32 fd, GPS_PPacket *packet) +{ + int32 n; + UC u; + UC *p; + int32 i; + int32 payload_size; + const char *m1; + const char *m2; + + garmin_usb_packet pkt; + memset(&pkt, 0, sizeof(pkt)); + n = gusb_cmd_get(&pkt, sizeof(pkt)); + + if (1 && gps_show_bytes) { + GPS_Diag("\nRx Data:[%d]",n); + for (i = 0; i < n; i++) + GPS_Diag("%02x ", pkt.dbuf[i]); + for (i = 0; i < n; i++) + GPS_Diag("%c", isalnum(pkt.dbuf[i]) ? pkt.dbuf[i] : '.'); + m1 = Get_Pkt_Type(pkt.gusb_pkt.pkt_id[0], pkt.gusb_pkt.databuf[0], &m2); + GPS_Diag("(%-8s%s)\n", m1, m2 ? m2 : ""); + + } + + /* + * Populate members of serial packet from USB packet. The + * copy here seems wasteful, but teaching all the callers about + * a structure with the "data" member being in a different place + * (Since the protocol packets was badly exposed in the core + * design of jeeps) is even more painful. + */ + (*packet)->type = le_read16(&pkt.gusb_pkt.pkt_id); + payload_size = le_read32(&pkt.gusb_pkt.datasz); + (*packet)->n = payload_size; + memcpy((*packet)->data, &pkt.gusb_pkt.databuf, payload_size); + return payload_size; +} diff --git a/jeeps/gpsusbsend.c b/jeeps/gpsusbsend.c new file mode 100644 index 000000000..89fce642f --- /dev/null +++ b/jeeps/gpsusbsend.c @@ -0,0 +1,55 @@ +/* + Form GarminUSB packets to send. + + Copyright (C) 2004 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include "gps.h" +#include +#include +#include "garminusb.h" + + +void GPS_Make_Packet_usb(GPS_PPacket *packet, UC type, UC *data, int16 n) +{ + garmin_usb_packet **up = (garmin_usb_packet**) packet; + + /* + * This is a little tacky that we're stuffing a garmin_usb_packet + * into a GPS_PPacket, but the packet is big enough and we have only + * a few places that really peek into this structure anyway... + */ + memset(*up, 0, sizeof **packet); + (*up)->gusb_pkt.type = 0x14; /* Garmin protocol layer */ + le_write16((*up)->gusb_pkt.pkt_id, type); + le_write32((*up)->gusb_pkt.datasz, n); + memcpy(&(*up)->gusb_pkt.databuf, data, n); + + return; +} + +int32 +GPS_Write_Packet_usb(int32 fd, GPS_PPacket packet) +{ + size_t ret, sz; + + garmin_usb_packet *gp = (garmin_usb_packet*) packet; + sz = le_read32(gp->gusb_pkt.datasz); + + return gusb_cmd_send(gp, sz + 12); +} diff --git a/jeeps/gpsusbwin.c b/jeeps/gpsusbwin.c new file mode 100644 index 000000000..26e21c980 --- /dev/null +++ b/jeeps/gpsusbwin.c @@ -0,0 +1,294 @@ +/* + Windows layer of Garmin/USB protocol. + + Copyright (C) 2004 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gps.h" +#include "gpsapp.h" +#include "garminusb.h" + +/* Constants from Garmin doc. */ + +// {2C9C45C2-8E7D-4C08-A12D-816BBAE722C0} +DEFINE_GUID(GARMIN_GUID, 0x2c9c45c2L, 0x8e7d, 0x4c08, 0xa1, 0x2d, 0x81, 0x6b, 0xba, 0xe7, 0x22, 0xc0); + +#define GARMIN_USB_API_VERSION 1 +#define GARMIN_USB_MAX_BUFFER_SIZE 4096 +#define GARMIN_USB_INTERRUPT_DATA_SIZE 64 + +#define IOCTL_GARMIN_USB_API_VERSION CTL_CODE \ + (FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_GARMIN_USB_INTERRUPT_IN CTL_CODE \ + (FILE_DEVICE_UNKNOWN, 0x850, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE CTL_CODE \ + (FILE_DEVICE_UNKNOWN, 0x851, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +static HANDLE *usb_handle ; +static int usb_tx_packet_size ; + +static const char oinit[12] = {0, 0, 0, 0, 0x05, 0, 0, 0, 0, 0, 0, 0}; +garmin_usb_packet iresp; +garmin_usb_packet iresp_junk; +static int ct; + +static char * id_unit(void); + +int +gusb_open(const char *pname) +{ + int maxtries; + int maxct = 10; + int unit_number = 0; + int req_unit_number = 0; + + SP_INTERFACE_DEVICE_DATA devinterface; + PSP_INTERFACE_DEVICE_DETAIL_DATA pdd = NULL; + SP_DEVINFO_DATA devinfo; + HDEVINFO hdevinfo; + DWORD size; + if (ct++) return 1; + + if (strlen(pname) > 4) { + req_unit_number = atoi(pname+4); + GPS_Diag("Searching for USB unit number %d\n", unit_number); +// if (req_unit_number == -2) { +// printf("%d %u %s\n", 0, 3019840053, "GPSMap60CS Software Version 3.50"); +// exit(0); +// } + } + + hdevinfo = SetupDiGetClassDevs( &GARMIN_GUID, NULL, NULL, + DIGCF_PRESENT|DIGCF_INTERFACEDEVICE); + + if (hdevinfo == INVALID_HANDLE_VALUE) { + fatal("XXX"); + return 0; + } + + /* Get the device associated with this index. */ + devinterface.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA); + if (!SetupDiEnumDeviceInterfaces(hdevinfo, NULL, &GARMIN_GUID, + 0, &devinterface)) { + fatal("blah"); + return 0; + } + + SetupDiGetDeviceInterfaceDetail(hdevinfo, &devinterface, + NULL, 0, &size, NULL); + + pdd = malloc(size); + pdd->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); + devinfo.cbSize = sizeof(SP_DEVINFO_DATA); + + if (!SetupDiGetDeviceInterfaceDetail(hdevinfo, &devinterface, pdd, size, NULL, &devinfo)) { + fatal("ZZZ"); + } + + /* Whew. All that just to get something we can open... */ + GPS_Diag("Windows GUID for interface %d is \n\t%s\n", unit_number, + pdd->DevicePath); + usb_handle = CreateFile(pdd->DevicePath, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL ); + if (usb_handle == INVALID_HANDLE_VALUE) { + GPS_Serial_Error("CreateFile failed"); + fatal("CreateFile"); + } + + if(!DeviceIoControl(usb_handle, IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE, NULL, 0, + &usb_tx_packet_size, GARMIN_USB_INTERRUPT_DATA_SIZE, &size, NULL)) { + fatal("Couldn't get USB packet size"); + } + + if (pdd) { + free(pdd); + } + SetupDiDestroyDeviceInfoList(hdevinfo); + + for (maxtries = maxct; maxtries; maxtries--) { + + le_write16(&iresp.gusb_pkt.pkt_id, 0); + le_write32(&iresp.gusb_pkt.datasz, 0); + le_write32(&iresp.gusb_pkt.databuf, 0); + + gusb_cmd_send((const garmin_usb_packet *) oinit, sizeof(oinit)); + gusb_cmd_get(&iresp, sizeof(iresp)); + + if ((le_read16(iresp.gusb_pkt.pkt_id) == 6) && + (le_read32(iresp.gusb_pkt.datasz) == 4)) { + unsigned serial_number = le_read32(iresp.gusb_pkt.databuf); + GPS_Diag("Serial %u. Synced in %d\n", + serial_number, + maxct - maxtries); + garmin_unit_info[unit_number].serial_number = serial_number; + garmin_unit_info[unit_number].os_identifier = strdup(pdd->DevicePath); + garmin_unit_info[unit_number].product_identifier = id_unit(); + if (req_unit_number < 0) { + printf("%d %u %s\n", unit_number, serial_number, garmin_unit_info[unit_number].product_identifier); + return 2; + } + return 1; + } + } + fatal("Unable to establish USB syncup within %d tries.\n", maxct); + return 0; +} + +int +gusb_close(void) +{ + if (usb_handle != INVALID_HANDLE_VALUE) { +#if 0 + /* FIXME: we should probably release things and delete the "ct" + * reference count above... + */ + CloseHandle(usb_handle); + usb_handle = INVALID_HANDLE_VALUE; +#endif + } +} + +int +gusb_cmd_get(garmin_usb_packet *ibuf, size_t sz) +{ + DWORD rxed = GARMIN_USB_INTERRUPT_DATA_SIZE; + unsigned char *buf = &ibuf->dbuf; + int i; + int tsz=0; + unsigned char *obuf = buf; + + while (sz) { + /* The driver wrongly (IMO) rejects reads smaller than + * GARMIN_USB_INTERRUPT_DATA_SIZE + */ + if(!DeviceIoControl(usb_handle, IOCTL_GARMIN_USB_INTERRUPT_IN, NULL, 0, + buf, GARMIN_USB_INTERRUPT_DATA_SIZE, &rxed, NULL)) { + GPS_Serial_Error("Ioctl"); + fatal("ioctl"); + } + buf += rxed; + sz =- rxed; + tsz += rxed; + if (rxed < GARMIN_USB_INTERRUPT_DATA_SIZE) { + break; + } + } + + if (gps_show_bytes) { + const char *m1, *m2; + printf("RX [%d]:", tsz); + for(i=0;igusb_pkt.pkt_id[0], ibuf->gusb_pkt.databuf[0], &m2); + GPS_Diag("(%-8s%s)\n", m1, m2 ? m2 : ""); + printf("\n"); + } + return tsz; +} + +int +gusb_cmd_send(const garmin_usb_packet *opkt, size_t sz) +{ + DWORD rsz; + size_t i; + unsigned char *obuf = &opkt->dbuf; + const char *m1, *m2; + + /* The spec warns us about making writes an exact multiple + * of the packet size, but isn't clear whether we can issue + * data in a single call to WriteFile if it spans buffers. + */ + WriteFile(usb_handle, obuf, sz, &rsz, NULL); + if (gps_show_bytes) { + printf("TX [%d]:", rsz); + for(i=0;igusb_pkt.pkt_id[0], opkt->gusb_pkt.databuf[0], &m2); + GPS_Diag("(%-8s%s)\n", m1, m2 ? m2 : ""); + } + + if (rsz != sz) { + fatal ("Error sending %d bytes. Successfully sent %d\n", sz, rsz); + } + + if (0 == sz % usb_tx_packet_size) { + DWORD sz2; + GPS_Diag("Writing padding buffer.\n"); + WriteFile(usb_handle, 0, 0, &sz2, NULL); + } + + return rsz; +} + +static char * +id_unit(void) +{ +static const char oid[12] = {20, 0, 0, 0, 0xfe, 0, 0, 0, 0, 0, 0, 0}; + /* + * Identify the unit before getting into all the protocol gunk. + * We get two packets back, but we discard the protocol array + * for now. + */ + + gusb_cmd_send((garmin_usb_packet *)oid, sizeof(oid)); + gusb_cmd_get(&iresp, sizeof(iresp)); + gusb_cmd_get(&iresp_junk, sizeof(iresp_junk)); + + if (iresp.gusb_pkt.type == 20 && + le_read16(iresp.gusb_pkt.pkt_id) == 0xff) { + return strdup(iresp.gusb_pkt.databuf+4); + } + + return NULL; +} + + +#if 0 +main() +{ + DWORD sz; +char ocmd[] = {00, 00, 00, 00, 05, 00, 00, 00, 00, 00, 00, 00}; +char ocmd2[] = {0x14, 00, 00, 00, 0xfe, 00, 00, 00, 00, 00, 00, 00}; + gusb_open(); + WriteFile(usb_handle, ocmd, sizeof(ocmd), &sz, NULL); + printf("Wrote %d\n", sz); + usb_intr_get(); + + WriteFile(usb_handle, ocmd2, sizeof(ocmd2), &sz, NULL); + printf("Wrote %d\n", sz); + usb_intr_get(); + + WriteFile(usb_handle, ocmd2, sizeof(ocmd2), &sz, NULL); + printf("Wrote %d\n", sz); + usb_intr_get(); +} +#endif -- 2.30.2